//
//  tdl/net/epoll_wrapper.h
//  Created by tommy on 13/04/25
//

#ifndef tdl_net_epoll_wrapper_h
#define tdl_net_epoll_wrapper_h

#ifdef SYS_EPOLL_H
#include <errno.h>
#include <sys/epoll.h>

#include "tdltypes.h"
#include "tdlexcept.h"
using namespace tdl;

namespace tdl {
///////////////
class epoll
{
public:
	enum {
		EPOLL_BLOCK		= -1, 
		EPOLL_NOWAIT	= 0
	};

	typedef std::pair<epoll_event*, uint32_t>	result;

protected:
	int			m_epfd;
	uint32_t	m_maxfd;
	epoll_event*	m_events;

protected:
	void _ctl(int fd, int act, int flag) throw(epoll_ex)
	{
		tdl_assert(m_epfd != -1);

		epoll_event ev;
		ev.data.fd = fd;
		ev.events = flag;

		int r = ::epoll_ctl(m_epfd, act, fd, &ev);
		if(r < 0)
		{
			int e = errno;
			if((e == EEXIST && act == EPOLL_CTL_ADD)
			   || (e == ENOENT && act == EPOLL_CTL_DEL)
			   || (e == ENOENT && act == EPOLL_CTL_MOD))
				return;
			else
				THROW2(epoll_ex, TRACE_INFO() << "epoll_ctl(" << m_epfd << "," << act << "," << fd << ")");
		}
	}

public:
	epoll() : m_epfd(-1), m_maxfd(0), m_events(NULL) {}

	~epoll()
	{
		if(m_events)
		{
			delete[] m_events;
			m_events = NULL;
		}
		::close(m_epfd);
	}

	void create(uint32_t nmax) throw(epoll_ex)
	{
		tdl_assert(nmax > 0);

		m_maxfd = nmax;

		m_epfd = ::epoll_create(nmax);
		if(m_epfd == -1)
			THROW2(epoll_ex, TRACE_INFO() << "epoll_create(" << nmax << ")");

		m_events = new epoll_event[nmax];
	}

	void add_fd(int fd, int flag = EPOLLIN | EPOLLET)
	{
		_ctl(fd, EPOLL_CTL_ADD, flag);
	}

	void del_fd(int fd)
	{
		_ctl(fd, EPOLL_CTL_DEL, 0);
	}

	result wait(int n_ms = EPOLL_BLOCK) throw(epoll_ex)
	{
		int n;
		tdl_assert(m_maxfd > 0);

		do {
			n = ::epoll_wait(m_epfd, m_events, m_maxfd, n_ms);
			if(n < 0 && errno != EINTR)
				THROW2(epoll_ex, TRACE_INFO() << "epoll_wait(" << m_epfd << ", m_events, " << m_maxfd << ", " << n_ms << ")");
		} while(n < 0);

		return result(m_events, n);
	}
};


///////////////
}	// end of namespace
#endif	// ifdef SYS_EPOLL_H

#endif

